# -*- coding: utf-8 -*-
#
#    Copyright (C) 2012 Gonzalo Bonigo <gonzalobonigo@gmail.com>
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see http://www.gnu.org/licenses/
#


"""Este archivo contiene la implemetacion del metodo quick_diag_analyzer()."""


__all__ = [
    "quick_diagnosability_analyzer"
]


import os.path
import time
import sys
import os

from lts import utils
import lts


TIME = 0
IS_DIAG = 1


def _print_heading(first_iteration, fault, verbose):
    """
        TODO: Add description.
    """
    if not first_iteration:
        print "\n--------------------------------------------------"
    print "\n-> Checkign diagnosability for fault", fault
    if verbose:
        print ""
    return False


def _check_subsystems_diagnosability(system, fault, verbose):
    """
        TODO: Add description.
    """
    for s in system:
        if verbose:
            print "Checking diagnosability for", s.name.replace('.aut', '')
        ds = s.is_diagnosable(set([fault]))
        if ds['is_diag']:
            if verbose:
                print "It is diagnosable."
        else:
            if verbose:
                print "It is non-diagnosable."
    print ""


def _create_sync_name(current_subsystem, system):
    """
        TODO: Add description.
    """
    SYNC_SEP = '||'
    sync_name_list = []
    for subsystem in system:
        if subsystem != current_subsystem:
            sync_name_list.append('R%s' % subsystem.name.replace('.aut', ''))
        else:
            sync_name_list.append(subsystem.name.replace('.aut', ''))
    return SYNC_SEP.join(sync_name_list)


def _check_system_diagnosability(system, fault, take_time, verbose):
    """
        TODO: Add description.
    """
    # Lista de tiempos.
    list_of_times = []
    # Dice si el sistema es diagnosticable o no.
    system_is_diagnosable = True
    for current_subsystem in system:
        # Cuenta el tiempo que toman todas las sincronizaciones.
        sync_time = 0
        # Armo un nombre para mostrar lo que estoy sincronizando.
        if verbose:
            sync_name = _create_sync_name(current_subsystem, system)
            print "Synchronizing:", sync_name
        # Esta variable almacena la composición de los subsistemas.
        compound_system = None
        # Compongo los subsistemas, reduciendo cuando es necesario.
        for subsystem in system:
            subsystem = subsystem.reduce_by_faults(set([fault])) if subsystem != current_subsystem else subsystem
            sync_time_1 = time.time()
            compound_system = subsystem if compound_system is None else compound_system.sync(subsystem, None, None, False, False)
            sync_time_2 = time.time()
            sync_time += sync_time_2 - sync_time_1
        # Analizo la diagnosticabilidad de la composición.
        if verbose:
            print "Checking diagnosability for", compound_system.name.replace('.aut', '')
            print "Building twin-plant..."
        analyze_time_1 = time.time()
        # Creo la twin-plant.
        compound_system.get_twinplant()
        if verbose: print "Analyzing..."
        # Me fijo si el sistema es diagnosticable o no.
        dc = compound_system.is_diagnosable(set([fault]))
        analyze_time_2 = time.time()
        if dc['is_diag']:
            if verbose: print "It is diagnosable."
            subsystem_diag = True
        else:
            if verbose: print "It is non-diagnosable."
            system_is_diagnosable = False
            subsystem_diag = False
        # Guardo el tiempo de la compoiscion y del análisis de diagnosticabilidad.
        if take_time:
            list_of_times.append(
                ((analyze_time_2 - analyze_time_1) + sync_time, subsystem_diag)
            )
            if verbose: print "Time:", utils.str_time((analyze_time_2 - analyze_time_1) + sync_time)
        if verbose: print ""
        # Guardo la composición en un archivo.
        lts.LTS.to_file(compound_system)
    return (list_of_times, system_is_diagnosable)


def _print_results(system_is_diagnosable, fault, list_of_times, take_time, verbose):
    """
        TODO: Add description.
    """
    if not verbose:
        print ""
    if system_is_diagnosable:
        print "=> The system is diagnosable for fault " + fault
        if take_time:
            times = [x[TIME] for x in list_of_times if x[IS_DIAG]]
            print "\nTime:", utils.str_time(max(times))
    else:
        print "=> The system is non-diagnosable for fault " + fault
        if take_time:
            times = [x[TIME] for x in list_of_times if not x[IS_DIAG]]
            print "\nTime:", utils.str_time(min(times))


def quick_diagnosability_analyzer(args):
    """
        TODO: Add description.
    """
    # Pongo los argumentos en variables para facilitar su acceso.
    system = args["system"]
    fault_set = args["fault_set"]
    take_time = args["time"]
    verbose = args["verbose"]
    # Esta variable se utiliza para la salida por pantalla.
    first_iteration = True
    # Analizo la diagnosticabilidad para cada falla.
    for fault in fault_set:
        # Imprime una cabecera cada vez que se comienza a diagnosticar una falla.
        first_iteration = _print_heading(first_iteration, fault, verbose)
        # Analizo la diagnosticabilidad de cada subsistema.
        _check_subsystems_diagnosability(system, fault, verbose)
        # Analizo la diagnosticabilidad del sistema global.
        list_of_times, system_is_diagnosable = _check_system_diagnosability(
            system,
            fault,
            take_time,
            verbose
        )
        # Imprime el resultado final del análisis.
        _print_results(system_is_diagnosable, fault, list_of_times, take_time, verbose)
